﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Diagnostics.Contracts;

namespace ReactiveCode.Expressions
{
    using Observables;
    using Util;

    [System.Diagnostics.DebuggerTypeProxy(typeof(Assignment<>.DebugView))]
    class Assignment<T> : ActionExpression<T>
    {
        #region DebugView

        class DebugView
        {
            Assignment<T> _assignment;
            public DebugView(Assignment<T> assignment)
            {
                Contract.Requires(assignment != null);
                _assignment = assignment;
            }

            public string Target
            {
                get { return _assignment.Target; }
            }
            public string Value
            {
                get { return _assignment.Value; }
            }
            public bool Enabled
            {
                get { return _assignment.Enabled; }
            }
        }

        #endregion

        ISubject<T> _target;
        internal readonly LatestValue<T> _latestExternal;

        public string Target { get; set; }
        public string Value { get; set; }

        protected override bool UndoBeforeDo
        {
            get { return false; }
        }

        public Assignment(ObservableFactory<ISubject<T>> target, ObservableFactory<IObservable<T>> value)
        {
            Contract.Requires(target != null && value != null);
            _target = Extract(target);
            _latestExternal = _target.LatestContinuous();
            _latestExternal.Subscribe(delegate
            {
                if (Enabled)
                    DoLast();
            });
            Construct(value);
        }
        public override void Dispose()
        {
            base.Dispose();
            _latestExternal.Dispose();
        }

        protected override void Do(T value)
        {
            _target.OnNext(value);
        }
        protected override void Undo(T arg)
        {
            base.Undo(arg);
            RestoreExternal();
        }
        protected override void OnDisabled()
        {
            RestoreExternal();
        }
        private void RestoreExternal()
        {
            if (_latestExternal.HasValue)
                _target.OnNext(_latestExternal.Value);
        }
    }
}